package com.aliothservice.catfish.admin.data;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;

import com.aliothservice.catfish.admin.MockupInboundAdapter;
import com.aliothservice.catfish.admin.request.AddIdentityTemplateRequest;
import com.aliothservice.catfish.admin.request.AddNoteToVehicleBindingRequestRequest;
import com.aliothservice.catfish.admin.request.AddNoteToWarehouseBindingRequestRequest;
import com.aliothservice.catfish.admin.request.AddServiceTemplateRequest;
import com.aliothservice.catfish.admin.request.BindVehicleRequest;
import com.aliothservice.catfish.admin.request.BindWarehouseRequest;
import com.aliothservice.catfish.admin.request.DeleteIdentityTemplateRequest;
import com.aliothservice.catfish.admin.request.DeleteServiceTemplateRequest;
import com.aliothservice.catfish.admin.request.FilterIndividualUsersRequest;
import com.aliothservice.catfish.admin.request.HeartbeatMockupRequest;
import com.aliothservice.catfish.admin.request.HumidityMockupRequest;
import com.aliothservice.catfish.admin.request.LocationMockupRequest;
import com.aliothservice.catfish.admin.request.MockupResultRequest;
import com.aliothservice.catfish.admin.request.ProcessedVehicleBindingRequestRequest;
import com.aliothservice.catfish.admin.request.ProcessedWarehouseBindingRequestRequest;
import com.aliothservice.catfish.admin.request.QueryIndividualUsersRequest;
import com.aliothservice.catfish.admin.request.QueryOnlineVehiclesRequest;
import com.aliothservice.catfish.admin.request.QueryOnlineWarehouseRequest;
import com.aliothservice.catfish.admin.request.QueryProductRequest;
import com.aliothservice.catfish.admin.request.QueryUserRequest;
import com.aliothservice.catfish.admin.request.QueryVehicleOrdersRequest;
import com.aliothservice.catfish.admin.request.QueryVehicleRequest;
import com.aliothservice.catfish.admin.request.QueryWarehouseRequest;
import com.aliothservice.catfish.admin.request.ResolveAppealRequest;
import com.aliothservice.catfish.admin.request.TemperatureMockupRequest;
import com.aliothservice.catfish.data.TransportationTemperature;
import com.aliothservice.catfish.device.data.LocationDocument;
import com.aliothservice.catfish.device.data.LocationObject;
import com.aliothservice.catfish.device.data.VehicleBinding;
import com.aliothservice.catfish.device.data.VehicleBindingRequest;
import com.aliothservice.catfish.device.data.WarehouseBinding;
import com.aliothservice.catfish.device.data.WarehouseBindingRequest;
import com.aliothservice.catfish.device.request.VehicleLocationDocumentRequest;
import com.aliothservice.catfish.identity.data.EnterprisePending;
import com.aliothservice.catfish.identity.data.IdentityDataManager;
import com.aliothservice.catfish.identity.data.IndividualPending;
import com.aliothservice.catfish.identity.data.User;
import com.aliothservice.catfish.identity.data.WithdrawRequest;
import com.aliothservice.catfish.message.MessageController;
import com.aliothservice.catfish.message.data.Message;
import com.aliothservice.catfish.order.data.CompleteVehicleOrder;
import com.aliothservice.catfish.order.data.ProductOrder;
import com.aliothservice.catfish.order.data.WarehouseOrder;
import com.aliothservice.catfish.product.data.Product;
import com.aliothservice.catfish.product.data.ProductPending;
import com.aliothservice.catfish.product.data.ProductSpec;
import com.aliothservice.catfish.product.data.Vehicle;
import com.aliothservice.catfish.product.data.VehiclePending;
import com.aliothservice.catfish.product.data.VehicleSpec;
import com.aliothservice.catfish.product.data.Warehouse;
import com.aliothservice.catfish.product.data.WarehousePending;
import com.aliothservice.catfish.product.data.WarehouseSpec;
import com.aliothservice.catfish.util.DateUtil;
import com.mongodb.WriteResult;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.socket.SocketChannel;
import push.AndroidNotification;
import push.PushClient;
import push.android.AndroidUnicast;
import push.ios.IOSUnicast;

public class AdminDataManager {

	public Admin findAdmin(String loginName) {
		Query query = new Query();
		Criteria criteria = Criteria.where("loginName").is(loginName);
		query.addCriteria(criteria);
		Admin admin = mongoTemplate.findOne(query, Admin.class);

		return admin;
	}

	public boolean updatePassword(String loginName, String password) {
		Query query = new Query();
		Criteria criteria = Criteria.where("loginName").is(loginName);
		query.addCriteria(criteria);
		Update update = new Update();
		update.set("password", password);

		WriteResult result = mongoTemplate.updateFirst(query, update, Admin.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}

		return true;
	}

	public Message rejectIndividualPending(String pendingId, String descriptionOfReject) {
		// locate individual pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		pendingQuery.addCriteria(pendingCriteria);
		IndividualPending pending = mongoTemplate.findOne(pendingQuery, IndividualPending.class);
		if (null == pending) {
			if (logger.isErrorEnabled()) {
				logger.error("rejectIndividualPending - cannot locate individual pending");
			}
			return null;
		}

		// check pending status
		if (0 != pending.getStatus().compareTo(IndividualPending.STATUS_READY_FOR_REVIEW)) {
			if (logger.isErrorEnabled()) {
				logger.error("rejectIndividualPending - invalid pending status");
			}
			return null;
		}

		Query query = new Query();
		Criteria criteria = Criteria.where("id").is(pendingId);
		query.addCriteria(criteria);
		Update update = new Update();
		update.set("descriptionOfReject", descriptionOfReject);
		update.set("status", IndividualPending.STATUS_REJECTED);
		update.set("lastUpdateTime", new Date());

		WriteResult result = mongoTemplate.updateFirst(query, update, IndividualPending.class);
		if (!result.isUpdateOfExisting()) {
			return null;
		}

		// update user
		Query userQuery = new Query();
		Criteria userCriteria = Criteria.where("phoneNumber").is(pending.getPhoneNumber());
		userQuery.addCriteria(userCriteria);
		Update userUpdate = new Update();
		pending.setStatus(IndividualPending.STATUS_REJECTED);
		userUpdate.set("rejectedIndividualInfo", pending);
		WriteResult userResult = mongoTemplate.updateFirst(userQuery, userUpdate, User.class);
		if (!userResult.isUpdateOfExisting()) {
			return null;
		}
		
		// compose message
		Message rejectMessage = new Message();
		rejectMessage.setId(UUID.randomUUID().toString());
		rejectMessage.setDate(new Date());
		rejectMessage.setTitle("个人认证未通过审核");
		rejectMessage.setContent("未通过审核原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
		rejectMessage.setPayload(pending.getPhoneNumber());
		rejectMessage.setFlag(Message.FLAG_RED);
		rejectMessage.setSourceModule("IdentityAdmin");
		rejectMessage.setSourceEvent("rejectIndividualPending");

		User user = identityDataManager.findUser(pending.getPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链审核");
							unicast.setTitle("个人认证未通过审核");
							unicast.setText("未通过审核原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("个人认证未通过审核");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}

		return rejectMessage;
	}

	public Message rejectEnterprisePending(String pendingId, String descriptionOfReject) {
		// locate enterprise pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		pendingQuery.addCriteria(pendingCriteria);
		EnterprisePending pending = mongoTemplate.findOne(pendingQuery, EnterprisePending.class);
		if (null == pending) {
			if (logger.isErrorEnabled()) {
				logger.error("rejectEnterprisePending - cannot locate enterprise pending");
			}
			return null;
		}

		// check pending status
		if (0 != pending.getStatus().compareTo(EnterprisePending.STATUS_READY_FOR_REVIEW)) {
			if (logger.isErrorEnabled()) {
				logger.error("rejectEnterprisePending - invalid pending status");
			}
			return null;
		}

		Query query = new Query();
		Criteria criteria = Criteria.where("id").is(pendingId);
		query.addCriteria(criteria);
		Update update = new Update();
		update.set("descriptionOfReject", descriptionOfReject);
		update.set("status", EnterprisePending.STATUS_REJECTED);
		update.set("lastUpdateTime", new Date());

		WriteResult result = mongoTemplate.updateFirst(query, update, EnterprisePending.class);
		if (!result.isUpdateOfExisting()) {
			return null;
		}
		
		// update user
		Query userQuery = new Query();
		Criteria userCriteria = Criteria.where("phoneNumber").is(pending.getPhoneNumber());
		userQuery.addCriteria(userCriteria);
		Update userUpdate = new Update();
		pending.setStatus(EnterprisePending.STATUS_REJECTED);
		userUpdate.set("rejectedEnterpriseInfo", pending);
		WriteResult userResult = mongoTemplate.updateFirst(userQuery, userUpdate, User.class);
		if (!userResult.isUpdateOfExisting()) {
			return null;
		}

		// compose message
		Message rejectMessage = new Message();
		rejectMessage.setId(UUID.randomUUID().toString());
		rejectMessage.setDate(new Date());
		rejectMessage.setTitle("企业认证未通过审核");
		rejectMessage.setContent("未通过审核原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
		rejectMessage.setPayload(pending.getPhoneNumber());
		rejectMessage.setFlag(Message.FLAG_RED);
		rejectMessage.setSourceModule("IdentityAdmin");
		rejectMessage.setSourceEvent("rejectEnterprisePending");
		
		User user = identityDataManager.findUser(pending.getPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链审核");
							unicast.setTitle("企业认证未通过审核");
							unicast.setText("未通过审核原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("企业认证未通过审核");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}

		return rejectMessage;
	}

	public Message approveIndividualPending(String pendingId) {
		// locate individual pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		pendingQuery.addCriteria(pendingCriteria);
		IndividualPending pending = mongoTemplate.findOne(pendingQuery, IndividualPending.class);
		if (null == pending) {
			if (logger.isErrorEnabled()) {
				logger.error("approveIndividualPending - cannot locate individual pending");
			}
			return null;
		}

		// check pending status
		if (0 != pending.getStatus().compareTo(IndividualPending.STATUS_READY_FOR_REVIEW)) {
			if (logger.isErrorEnabled()) {
				logger.error("approveIndividualPending - invalid pending status");
			}
			return null;
		}

		// update pending status
		Query updateQuery = new Query();
		Criteria updateCriteria = Criteria.where("id").is(pendingId);
		updateQuery.addCriteria(updateCriteria);
		Update statusUpdate = new Update();
		statusUpdate.set("status", IndividualPending.STATUS_ACCEPTED);
		statusUpdate.set("lastUpdateTime", new Date());
		WriteResult statusResult = mongoTemplate.updateFirst(updateQuery, statusUpdate, IndividualPending.class);
		if (!statusResult.isUpdateOfExisting()) {
			return null;
		}

		// update user
		Query userQuery = new Query();
		Criteria userCriteria = Criteria.where("phoneNumber").is(pending.getPhoneNumber());
		userQuery.addCriteria(userCriteria);
		Update userUpdate = new Update();
		pending.setStatus(IndividualPending.STATUS_ACCEPTED);
		userUpdate.set("individualInfo", pending);
		userUpdate.set("accountStatus", User.ACCOUNT_STATUS_VERIFIED);
		WriteResult userResult = mongoTemplate.updateFirst(userQuery, userUpdate, User.class);
		if (!userResult.isUpdateOfExisting()) {
			return null;
		}

		// compose message
		Message approvalMessage = new Message();
		approvalMessage.setId(UUID.randomUUID().toString());
		approvalMessage.setDate(new Date());
		approvalMessage.setTitle("个人认证审核通过");
		approvalMessage.setContent("恭喜您通过个人认证！接下来，您可以发布车源，库源和货源。");
		approvalMessage.setPayload(pending.getPhoneNumber());
		approvalMessage.setFlag(Message.FLAG_BLUE);
		approvalMessage.setSourceModule("IdentityAdmin");
		approvalMessage.setSourceEvent("approveIndividualPending");
		
		User user = identityDataManager.findUser(pending.getPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链审核");
							unicast.setTitle("个人认证审核通过");
							unicast.setText("恭喜您通过个人认证！接下来，您可以发布车源，库源和货源。");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("个人认证审核通过");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}

		return approvalMessage;
	}

	public Message approveEnterprisePending(String pendingId) {
		// locate enterprise pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		pendingQuery.addCriteria(pendingCriteria);
		EnterprisePending pending = mongoTemplate.findOne(pendingQuery, EnterprisePending.class);
		if (null == pending) {
			if (logger.isErrorEnabled()) {
				logger.error("approveEnterprisePending - cannot locate enterprise pending");
			}
			return null;
		}

		// check pending status
		if (0 != pending.getStatus().compareTo(EnterprisePending.STATUS_READY_FOR_REVIEW)) {
			if (logger.isErrorEnabled()) {
				logger.error("approveEnterprisePending - invalid pending status");
			}
			return null;
		}

		// update pending status
		Query updateQuery = new Query();
		Criteria updateCriteria = Criteria.where("id").is(pendingId);
		updateQuery.addCriteria(updateCriteria);
		Update statusUpdate = new Update();
		statusUpdate.set("status", EnterprisePending.STATUS_ACCEPTED);
		statusUpdate.set("lastUpdateTime", new Date());
		WriteResult statusResult = mongoTemplate.updateFirst(updateQuery, statusUpdate, EnterprisePending.class);
		if (!statusResult.isUpdateOfExisting()) {
			return null;
		}

		// update user
		Query userQuery = new Query();
		Criteria userCriteria = Criteria.where("phoneNumber").is(pending.getPhoneNumber());
		userQuery.addCriteria(userCriteria);
		Update userUpdate = new Update();
		pending.setStatus(EnterprisePending.STATUS_ACCEPTED);
		userUpdate.set("enterpriseInfo", pending);
		userUpdate.set("accountStatus", User.ACCOUNT_STATUS_VERIFIED);
		WriteResult userResult = mongoTemplate.updateFirst(userQuery, userUpdate, User.class);
		if (!userResult.isUpdateOfExisting()) {
			return null;
		}

		// compose message
		Message approvalMessage = new Message();
		approvalMessage.setId(UUID.randomUUID().toString());
		approvalMessage.setDate(new Date());
		approvalMessage.setTitle("企业认证审核通过");
		approvalMessage.setContent("恭喜您通过企业认证！接下来，您可以发布车源，库源和货源。");
		approvalMessage.setPayload(pending.getPhoneNumber());
		approvalMessage.setFlag(Message.FLAG_BLUE);
		approvalMessage.setSourceModule("IdentityAdmin");
		approvalMessage.setSourceEvent("approveEnterprisePending");
		
		User user = identityDataManager.findUser(pending.getPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链审核");
							unicast.setTitle("企业认证审核通过");
							unicast.setText("恭喜您通过企业认证！接下来，您可以发布车源，库源和货源。");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("企业认证审核通过");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}

		return approvalMessage;
	}
	
	public List<Map<String, Object>> listUsers(Pageable pageable) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria criteria = Criteria.where("accountStatus").ne(User.ACCOUNT_STATUS_FROZEN);
		query.addCriteria(criteria);
		query.with(pageable);
		List<User> list = mongoTemplate.find(query, User.class);
		for (User user : list) {
			if (logger.isDebugEnabled()) {
				logger.debug("listUsers - user: " + user);
			}
			result.add(user.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> withdrawRequestList() {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria criteria = Criteria.where("status").is(WithdrawRequest.STATUS_REQUESTED);
		query.addCriteria(criteria);
		List<WithdrawRequest> list = mongoTemplate.find(query, WithdrawRequest.class);
		for (WithdrawRequest request : list) {
			result.add(request.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> resolvedAppealList(Pageable pageable) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		query.with(pageable);
		List<AppealResult> list = mongoTemplate.find(query, AppealResult.class);
		for (AppealResult appealResult : list) {
			result.add(appealResult.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> listIndividualUsers(Pageable pageable) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

		Query query = new Query();
		Criteria individualCriteria = Criteria.where("individualInfo").exists(true);
		Criteria statusCriteria = Criteria.where("accountStatus").ne(User.ACCOUNT_STATUS_FROZEN);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(individualCriteria, statusCriteria);
		query.addCriteria(andCriteria);
		query.with(pageable);
		List<User> list = mongoTemplate.find(query, User.class);
		for (User user : list) {
			result.add(user.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> filterIndividualUsers(FilterIndividualUsersRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria individualCriteria = Criteria.where("individualInfo").exists(true);
		Criteria statusCriteria = Criteria.where("accountStatus").ne(User.ACCOUNT_STATUS_FROZEN);
		Criteria provinceCriteria = Criteria.where("individualInfo.province").is(req.getProvince());
		Criteria cityCriteria = Criteria.where("individualInfo.city").is(req.getCity());
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(individualCriteria, statusCriteria, provinceCriteria, cityCriteria);
		query.addCriteria(andCriteria);
		query.with(new PageRequest(0, 100));
		List<User> list = mongoTemplate.find(query, User.class);
		for (User user : list) {
			result.add(user.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> filterEnterpriseUsers(FilterIndividualUsersRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria individualCriteria = Criteria.where("enterpriseInfo").exists(true);
		Criteria statusCriteria = Criteria.where("accountStatus").ne(User.ACCOUNT_STATUS_FROZEN);
		Criteria provinceCriteria = Criteria.where("enterpriseInfo.province").is(req.getProvince());
		Criteria cityCriteria = Criteria.where("enterpriseInfo.city").is(req.getCity());
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(individualCriteria, statusCriteria, provinceCriteria, cityCriteria);
		query.addCriteria(andCriteria);
		query.with(new PageRequest(0, 100));
		List<User> list = mongoTemplate.find(query, User.class);
		for (User user : list) {
			result.add(user.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, String>> filterVehicles(FilterIndividualUsersRequest req) {
		List<Map<String, String>> result = new ArrayList<Map<String, String>>();
		
		Query query = new Query();
		Criteria provinceCriteria = Criteria.where("province").is(req.getProvince());
		Criteria cityCriteria = Criteria.where("city").is(req.getCity());
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(provinceCriteria, cityCriteria);
		query.addCriteria(andCriteria);
		query.with(new PageRequest(0, 100));
		List<Vehicle> list = mongoTemplate.find(query, Vehicle.class);
		for (Vehicle vehicle : list) {
			result.add(vehicle.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> filterWarehouses(FilterIndividualUsersRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria provinceCriteria = Criteria.where("province").is(req.getProvince());
		Criteria cityCriteria = Criteria.where("city").is(req.getCity());
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(provinceCriteria, cityCriteria);
		query.addCriteria(andCriteria);
		query.with(new PageRequest(0, 100));
		List<Warehouse> list = mongoTemplate.find(query, Warehouse.class);
		for (Warehouse warehouse : list) {
			result.add(warehouse.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> filterProducts(FilterIndividualUsersRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria provinceCriteria = Criteria.where("province").is(req.getProvince());
		Criteria cityCriteria = Criteria.where("city").is(req.getCity());
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(provinceCriteria, cityCriteria);
		query.addCriteria(andCriteria);
		query.with(new PageRequest(0, 100));
		List<Product> list = mongoTemplate.find(query, Product.class);
		for (Product product : list) {
			result.add(product.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> listEnterpriseUsers(Pageable pageable) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

		Query query = new Query();
		Criteria enterpriseCriteria = Criteria.where("enterpriseInfo").exists(true);
		Criteria statusCriteria = Criteria.where("accountStatus").ne(User.ACCOUNT_STATUS_FROZEN);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(enterpriseCriteria, statusCriteria);
		query.addCriteria(andCriteria);
		query.with(pageable);
		List<User> list = mongoTemplate.find(query, User.class);
		for (User user : list) {
			result.add(user.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> listAppealRequests() {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query vehicleQuery = new Query();
		Criteria vehicleCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_APPEAL_ONGOING);
		vehicleQuery.addCriteria(vehicleCriteria);
		List<CompleteVehicleOrder> vehicleList = mongoTemplate.find(vehicleQuery, CompleteVehicleOrder.class);
		for (CompleteVehicleOrder vehicleOrder : vehicleList) {
			result.add(vehicleOrder.toMap(null));
		}
		
		Query warehouseQuery = new Query();
		Criteria warehouseCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_APPEAL_ONGOING);
		warehouseQuery.addCriteria(warehouseCriteria);
		List<WarehouseOrder> warehouseList = mongoTemplate.find(warehouseQuery, WarehouseOrder.class);
		for (WarehouseOrder warehouseOrder : warehouseList) {
			result.add(warehouseOrder.toMap(null));
		}
		
		Query productQuery = new Query();
		Criteria productCriteria = Criteria.where("status").is(ProductOrder.STATUS_APPEAL_ONGOING);
		productQuery.addCriteria(productCriteria);
		List<ProductOrder> productList = mongoTemplate.find(productQuery, ProductOrder.class);
		for (ProductOrder productOrder : productList) {
			result.add(productOrder.toMap(null));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> identityTemplateList() {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		List<AdminIdentityTemplate> list = mongoTemplate.findAll(AdminIdentityTemplate.class);
		for (AdminIdentityTemplate template : list) {
			result.add(template.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> serviceTemplateList() {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		List<AdminServiceTemplate> list = mongoTemplate.findAll(AdminServiceTemplate.class);
		for (AdminServiceTemplate template : list) {
			result.add(template.toMap());
		}
		
		return result;
	}

	public List<Map<String, String>> individualPendingList(Pageable pageable) {
		List<Map<String, String>> list = new ArrayList<Map<String, String>>();

		Query query = new Query();
		Criteria criteria = Criteria.where("status").is(IndividualPending.STATUS_READY_FOR_REVIEW);
		query.addCriteria(criteria);
		if (null != pageable) {
			query.with(pageable);
		} else {
			query.with(new PageRequest(0, 100));
		}
		List<IndividualPending> pendingList = mongoTemplate.find(query, IndividualPending.class);
		for (IndividualPending pending : pendingList) {
			Map<String, String> map = new HashMap<String, String>();
			map.put("id", pending.getId());
			map.put("legalName", pending.getLegalName());
			map.put("phoneNumber", pending.getPhoneNumber());
			map.put("province", pending.getProvince());
			map.put("city", pending.getCity());
			map.put("district", pending.getDistrict());
			map.put("address", pending.getAddress());
			map.put("emergencyContactName", pending.getEmergencyContactName());
			map.put("emergencyContactPhoneNumber", pending.getEmergencyContactPhoneNumber());
			map.put("frontImageId", pending.getFrontImageId());
			map.put("backImageId", pending.getBackImageId());
			map.put("recentPhotoId", pending.getRecentPhotoId());
			DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
			map.put("lastUpdateTime", formatter.format(pending.getLastUpdateTime()));
			list.add(map);
		}

		return list;
	}

	public List<Map<String, String>> enterprisePendingList(Pageable pageable) {
		List<Map<String, String>> list = new ArrayList<Map<String, String>>();

		Query query = new Query();
		Criteria criteria = Criteria.where("status").is(EnterprisePending.STATUS_READY_FOR_REVIEW);
		query.addCriteria(criteria);
		if (null != pageable) {
			query.with(pageable);
		} else {
			query.with(new PageRequest(0, 100));
		}
		List<EnterprisePending> pendingList = mongoTemplate.find(query, EnterprisePending.class);
		for (EnterprisePending pending : pendingList) {
			list.add(pending.toMap());
		}

		return list;
	}
	
	public List<Map<String, Object>> vehicleBindingRequestList(Pageable pageable) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

		Query query = new Query();
		Criteria criteria = Criteria.where("status").is(VehicleBindingRequest.STATUS_OPEN);
		query.addCriteria(criteria);
		query.with(pageable);
		List<VehicleBindingRequest> list = mongoTemplate.find(query, VehicleBindingRequest.class);
		for (VehicleBindingRequest bindingRequest : list) {
			result.add(bindingRequest.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> warehouseBindingRequestList(Pageable pageable) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

		Query query = new Query();
		Criteria criteria = Criteria.where("status").is(WarehouseBindingRequest.STATUS_OPEN);
		query.addCriteria(criteria);
		query.with(pageable);
		List<WarehouseBindingRequest> list = mongoTemplate.find(query, WarehouseBindingRequest.class);
		for (WarehouseBindingRequest bindingRequest : list) {
			result.add(bindingRequest.toMap());
		}
		
		return result;
	}

	public List<Map<String, String>> vehiclePendingList(Pageable pageable) {
		List<Map<String, String>> list = new ArrayList<Map<String, String>>();

		Query query = new Query();
		Criteria criteria = Criteria.where("status").is(VehiclePending.STATUS_READY_FOR_REVIEW);
		query.addCriteria(criteria);
		if (null != pageable) {
			query.with(pageable);
		} else {
			query.with(new PageRequest(0, 100));
		}

		List<VehiclePending> pendingList = mongoTemplate.find(query, VehiclePending.class);
		for (VehiclePending pending : pendingList) {
			list.add(pending.toMap());
		}
		return list;
	}
	
	public List<Map<String, Object>> warehousePendingList(Pageable pageable) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria criteria = Criteria.where("status").is(WarehousePending.STATUS_READY_FOR_REVIEW);
		query.addCriteria(criteria);
		if (null != pageable) {
			query.with(pageable);
		} else {
			query.with(new PageRequest(0, 100));
		}

		List<WarehousePending> pendingList = mongoTemplate.find(query, WarehousePending.class);
		for (WarehousePending pending : pendingList) {
			result.add(pending.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> productPendingList(Pageable pageable) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria criteria = Criteria.where("status").is(ProductPending.STATUS_READY_FOR_REVIEW);
		query.addCriteria(criteria);
		if (null != pageable) {
			query.with(pageable);
		} else {
			query.with(new PageRequest(0, 100));
		}
		
		List<ProductPending> pendingList = mongoTemplate.find(query, ProductPending.class);
		for (ProductPending pending : pendingList) {
			result.add(pending.toMap());
		}
		
		return result;
	}
	
	public Message rejectWithdrawRequest(String requestId, String descriptionOfReject) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(requestId);
		Criteria statusCriteria = Criteria.where("status").is(WithdrawRequest.STATUS_REQUESTED);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, statusCriteria);
		query.addCriteria(andCriteria);
		WithdrawRequest request = mongoTemplate.findOne(query, WithdrawRequest.class);
		Update update = new Update();
		update.set("status", WithdrawRequest.STATUS_REJECTED);
		update.set("descriptionOfReject", descriptionOfReject);
		if (!mongoTemplate.updateFirst(query, update, WithdrawRequest.class).isUpdateOfExisting()) {
			return null;
		}
		
		// update user
		Query userQuery = new Query();
		Criteria userPhoneNumberCriteria = Criteria.where("phoneNumber").is(request.getUserId());
		userQuery.addCriteria(userPhoneNumberCriteria);
		Update userUpdate = new Update();
		userUpdate.set("withdrawStatus", User.WITHDRAW_STATUS_OPEN_FOR_WITHDRAW);
		if (!mongoTemplate.updateFirst(userQuery, userUpdate, User.class).isUpdateOfExisting()) {
			logger.error("rejectWithdrawRequest - cannot update user account status");
			return null;
		}
		
		// compose message
		Message rejectMessage = new Message();
		rejectMessage.setId(UUID.randomUUID().toString());
		rejectMessage.setDate(new Date());
		rejectMessage.setTitle("您的提现申请未通过审核");
		rejectMessage.setContent("未通过原因：" + descriptionOfReject + " 请重新提交审核");
		rejectMessage.setPayload(request.getUserId());
		rejectMessage.setFlag(Message.FLAG_RED);
		rejectMessage.setSourceModule("ProductAdmin");
		rejectMessage.setSourceEvent("rejectWithdrawRequest");

		return rejectMessage;
	}

	public Message rejectVehiclePending(String pendingId, String descriptionOfReject) {
		// locate vehicle pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		pendingQuery.addCriteria(pendingCriteria);
		VehiclePending pending = mongoTemplate.findOne(pendingQuery, VehiclePending.class);
		if (null == pending) {
			if (logger.isErrorEnabled()) {
				logger.error("rejectVehiclePending - cannot locate vehicle pending, " + pendingId);
			}
			return null;
		}

		// update vehicle pending
		Query updatePendingQuery = new Query();
		Criteria updatePendingCriteria = Criteria.where("id").is(pendingId);
		updatePendingQuery.addCriteria(updatePendingCriteria);
		Update updatePendingUpdate = new Update();
		updatePendingUpdate.set("descriptionOfReject", descriptionOfReject);
		updatePendingUpdate.set("status", VehiclePending.STATUS_REJECTED);
		updatePendingUpdate.set("lastUpdateTime", new Date());
		WriteResult updateResult = mongoTemplate.updateFirst(updatePendingQuery, updatePendingUpdate,
				VehiclePending.class);
		if (!updateResult.isUpdateOfExisting()) {
			return null;
		}

		// upsert vehicle
		Query upsertQuery = new Query();
		Criteria userCriteria = Criteria.where("userId").is(pending.getUserId());
		Criteria plateCriteria = Criteria.where("licensePlate").is(pending.getLicensePlate());
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(userCriteria, plateCriteria);
		upsertQuery.addCriteria(andCriteria);
		pending.setDescriptionOfReject(descriptionOfReject);
		Update upsertUpdate = Vehicle.updateFromVehiclePending(pending);
		upsertUpdate.set("status", Vehicle.VEHICLE_STATUS_REJECTED);
		WriteResult upsertResult = mongoTemplate.upsert(upsertQuery, upsertUpdate, Vehicle.class);
		if (1 != upsertResult.getN()) {
			return null;
		}

		// compose message
		Message rejectMessage = new Message();
		rejectMessage.setId(UUID.randomUUID().toString());
		rejectMessage.setDate(new Date());
		rejectMessage.setTitle("您的车辆（" + pending.getLicensePlate() + "）未通过审核");
		rejectMessage.setContent("车辆审核未通过原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
		rejectMessage.setPayload(pending.getUserId());
		rejectMessage.setFlag(Message.FLAG_RED);
		rejectMessage.setSourceModule("ProductAdmin");
		rejectMessage.setSourceEvent("rejectVehiclePending");
		
		User user = identityDataManager.findUser(pending.getUserId());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链审核");
							unicast.setTitle("您的车辆（" + pending.getLicensePlate() + "）未通过审核");
							unicast.setText("车辆审核未通过原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("您的车辆（" + pending.getLicensePlate() + "）未通过审核");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}

		return rejectMessage;
	}
	
	public Message rejectWarehousePending(String pendingId, String descriptionOfReject) {
		// locate warehouse pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		Criteria pendingStatusCriteria = Criteria.where("status").is(WarehousePending.STATUS_READY_FOR_REVIEW);
		Criteria andPendingCriteria = new Criteria();
		andPendingCriteria.andOperator(pendingCriteria, pendingStatusCriteria);
		pendingQuery.addCriteria(andPendingCriteria);
		WarehousePending pending = mongoTemplate.findOne(pendingQuery, WarehousePending.class);
		if (null == pending) {
			if (logger.isErrorEnabled()) {
				logger.error("rejectWarehousePending - cannot locate warehouse pending, " + pendingId);
			}
			return null;
		}
		
		// update warehouse pending
		Query updatePendingQuery = new Query();
		Criteria updatePendingCriteria = Criteria.where("id").is(pendingId);
		updatePendingQuery.addCriteria(updatePendingCriteria);
		Update updatePendingUpdate = new Update();
		updatePendingUpdate.set("descriptionOfReject", descriptionOfReject);
		updatePendingUpdate.set("status", WarehousePending.STATUS_REJECTED);
		updatePendingUpdate.set("lastUpdateTime", new Date());
		WriteResult updateResult = mongoTemplate.updateFirst(updatePendingQuery, updatePendingUpdate,
				WarehousePending.class);
		if (!updateResult.isUpdateOfExisting()) {
			return null;
		}
		
		// upsert warehouse
		Query upsertQuery = new Query();
		Criteria userCriteria = Criteria.where("userId").is(pending.getUserId());
		Criteria nameCriteria = Criteria.where("name").is(pending.getName());
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(userCriteria, nameCriteria);
		upsertQuery.addCriteria(andCriteria);
		Update upsertUpdate = Warehouse.updateFromWarehousePending(pending);
		upsertUpdate.set("status", Warehouse.WAREHOUSE_STATUS_REJECTED);
		upsertUpdate.set("descriptionOfReject", descriptionOfReject);
		WriteResult upsertResult = mongoTemplate.upsert(upsertQuery, upsertUpdate, Warehouse.class);
		if (1 != upsertResult.getN()) {
			return null;
		}
		
		// compose message
		Message rejectMessage = new Message();
		rejectMessage.setId(UUID.randomUUID().toString());
		rejectMessage.setDate(new Date());
		rejectMessage.setTitle("您的冷库（" + pending.getName() + "）未通过审核");
		rejectMessage.setContent("冷库审核未通过原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
		rejectMessage.setPayload(pending.getUserId());
		rejectMessage.setFlag(Message.FLAG_RED);
		rejectMessage.setSourceModule("ProductAdmin");
		rejectMessage.setSourceEvent("rejectWarehousePending");
		
		User user = identityDataManager.findUser(pending.getUserId());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链审核");
							unicast.setTitle("您的冷库（" + pending.getName() + "）未通过审核");
							unicast.setText("冷库审核未通过原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("您的冷库（" + pending.getName() + "）未通过审核");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}

		return rejectMessage;
	}
	
	public Message rejectProductPending(String pendingId, String descriptionOfReject) {
		// locate product pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		Criteria pendingStatusCriteria = Criteria.where("status").is(ProductPending.STATUS_READY_FOR_REVIEW);
		Criteria andPendingCriteria = new Criteria();
		andPendingCriteria.andOperator(pendingCriteria, pendingStatusCriteria);
		pendingQuery.addCriteria(andPendingCriteria);
		ProductPending pending = mongoTemplate.findOne(pendingQuery, ProductPending.class);
		if (null == pending) {
			if (logger.isErrorEnabled()) {
				logger.error("rejectProductPending - cannot locate product pending, " + pendingId);
			}
			return null;
		}
		
		// update product pending
		Query updatePendingQuery = new Query();
		Criteria updatePendingCriteria = Criteria.where("id").is(pendingId);
		updatePendingQuery.addCriteria(updatePendingCriteria);
		Update updatePendingUpdate = new Update();
		updatePendingUpdate.set("descriptionOfReject", descriptionOfReject);
		updatePendingUpdate.set("status", WarehousePending.STATUS_REJECTED);
		updatePendingUpdate.set("lastUpdateTime", new Date());
		WriteResult updateResult = mongoTemplate.updateFirst(updatePendingQuery, updatePendingUpdate,
				ProductPending.class);
		if (!updateResult.isUpdateOfExisting()) {
			return null;
		}
		
		// upsert product
		Query upsertQuery = new Query();
		Criteria userCriteria = Criteria.where("userId").is(pending.getUserId());
		Criteria nameCriteria = Criteria.where("name").is(pending.getName());
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(userCriteria, nameCriteria);
		upsertQuery.addCriteria(andCriteria);
		Update upsertUpdate = Product.updateFromProductPending(pending);
		upsertUpdate.set("status", Product.PRODUCT_STATUS_REJECTED);
		upsertUpdate.set("descriptionOfReject", descriptionOfReject);
		WriteResult upsertResult = mongoTemplate.upsert(upsertQuery, upsertUpdate, Product.class);
		if (1 != upsertResult.getN()) {
			return null;
		}
		
		// compose message
		Message rejectMessage = new Message();
		rejectMessage.setId(UUID.randomUUID().toString());
		rejectMessage.setDate(new Date());
		rejectMessage.setTitle("您的货品（" + pending.getName() + "）未通过审核");
		rejectMessage.setContent("货品审核未通过原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
		rejectMessage.setPayload(pending.getUserId());
		rejectMessage.setFlag(Message.FLAG_RED);
		rejectMessage.setSourceModule("ProductAdmin");
		rejectMessage.setSourceEvent("rejectProductPending");
		
		User user = identityDataManager.findUser(pending.getUserId());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链审核");
							unicast.setTitle("您的货品（" + pending.getName() + "）未通过审核");
							unicast.setText("货品审核未通过原因：" + descriptionOfReject + "请修改相关信息重新提交审核");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("您的货品（" + pending.getName() + "）未通过审核");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}

		return rejectMessage;
	}
	
	public boolean approveWithdrawRequest(String requestId) {
//		Query query = new Query();
//		Criteria idCriteria = Criteria.where("id").is(requestId);
//		Criteria statusCriteria = Criteria.where("status").is(WithdrawRequest.STATUS_REQUESTED);
//		Criteria andCriteria = new Criteria();
//		andCriteria.andOperator(idCriteria, statusCriteria);
//		query.addCriteria(andCriteria);
//		Update update = new Update();
//		update.set("status", WithdrawRequest.STATUS_PROCESSING);
//		if (!mongoTemplate.updateFirst(query, update, WithdrawRequest.class).isUpdateOfExisting()) {
//			return false;
//		}
		Query query = new Query();
		Criteria userCriteria = Criteria.where("id").is(requestId);
		Criteria statusCriteria = Criteria.where("status").is(WithdrawRequest.STATUS_REQUESTED);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(userCriteria, statusCriteria);
		query.addCriteria(andCriteria);
		WithdrawRequest request = mongoTemplate.findOne(query, WithdrawRequest.class);
		if (null == request) {
			logger.error("approveWithdrawRequest - cannot locate WithdrawRequest object, invalid state");
			return false;
		}
		Update update = new Update();
		update.set("status", WithdrawRequest.STATUS_PROCESSED);
		if (!mongoTemplate.updateFirst(query, update, WithdrawRequest.class).isUpdateOfExisting()) {
			logger.error("approveWithdrawRequest - cannot update withdraw request object");
			return false;
		}
		
		// update user account info
		Query userQuery = new Query();
		Criteria userIdCriteria = Criteria.where("phoneNumber").is(request.getUserId());
		userQuery.addCriteria(userIdCriteria);
		Update userUpdate = new Update();
		userUpdate.inc("balance", Float.valueOf(request.getWithdrawAmount().floatValue()*-1));
		userUpdate.inc("withdrawBalance", Float.valueOf(request.getWithdrawAmount().floatValue()*-1));
		userUpdate.set("withdrawStatus", User.WITHDRAW_STATUS_OPEN_FOR_WITHDRAW);
		if (!mongoTemplate.updateFirst(userQuery, userUpdate, User.class).isUpdateOfExisting()) {
			logger.error("approveWithdrawRequest - cannot update user account info");
			return false;
		}
		
		return true;
	}

	public Message approveVehiclePending(String pendingId, Date expiredBy) {
		// locate vehicle pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		Criteria statusPendingCriteria = Criteria.where("status").is(VehiclePending.STATUS_READY_FOR_REVIEW);
		Criteria andPendingCriteria = new Criteria();
		andPendingCriteria.andOperator(pendingCriteria, statusPendingCriteria);
		pendingQuery.addCriteria(andPendingCriteria);
		VehiclePending pending = mongoTemplate.findOne(pendingQuery, VehiclePending.class);
		if (null == pending) {
			if (logger.isErrorEnabled()) {
				logger.error("approveVehiclePending - cannot locate vehicle pending, " + pendingId);
			}
			return null;
		}

		// update vehicle pending
		Query updatePendingQuery = new Query();
		Criteria updatePendingCriteria = Criteria.where("id").is(pendingId);
		updatePendingQuery.addCriteria(updatePendingCriteria);
		Update updatePendingUpdate = new Update();
		updatePendingUpdate.set("status", VehiclePending.STATUS_ACCEPTED);
		updatePendingUpdate.set("lastUpdateTime", new Date());
		WriteResult updateResult = mongoTemplate.updateFirst(updatePendingQuery, updatePendingUpdate,
				VehiclePending.class);
		if (!updateResult.isUpdateOfExisting()) {
			return null;
		}

		// locate user
		User user = identityDataManager.findUser(pending.getUserId());
		if (null == user) {
			return null;
		}
		
		// upsert vehicle
		Query upsertQuery = new Query();
		Criteria userCriteria = Criteria.where("userId").is(pending.getUserId());
		Criteria plateCriteria = Criteria.where("licensePlate").is(pending.getLicensePlate());
		Criteria andUpsertCriteria = new Criteria();
		andUpsertCriteria.andOperator(userCriteria, plateCriteria);
		upsertQuery.addCriteria(andUpsertCriteria);
		pending.setExpiredBy(expiredBy);
		Update upsertUpdate = Vehicle.updateFromVehiclePending(pending);
		upsertUpdate.set("status", Vehicle.VEHICLE_STATUS_APPROVED);
		if (null != user.getIndividualInfo()) {
			upsertUpdate.set("individual", user.getIndividualInfo().getLegalName());
		}
		if (null != user.getEnterpriseInfo()) {
			upsertUpdate.set("enterprise", user.getEnterpriseInfo().getEnterpriseName());
		}
		WriteResult upsertResult = mongoTemplate.upsert(upsertQuery, upsertUpdate, Vehicle.class);
		if (upsertResult.isUpdateOfExisting()) {
			logger.warn("approveVehiclePending - approve vehicle pending with the same license plate: " + pending.getLicensePlate());
			return null;
		}
		if (1 != upsertResult.getN()) {
			return null;
		}

		// create vehicle spec and update stock
		List<TransportationTemperature> ttList = mongoTemplate.findAll(TransportationTemperature.class);
		ttList.sort(new Comparator<TransportationTemperature>() {
			@Override
			public int compare(TransportationTemperature ltt, TransportationTemperature rtt) {
				if (ltt.getTemperatureMin().intValue() <= rtt.getTemperatureMin().intValue()) {
					return -1;
				} else {
					return 1;
				}
			}
		});
		List<String> temperatureSpec = new ArrayList<String>();
		for (TransportationTemperature tt : ttList) {
			if ((pending.getTemperatureMin().intValue()<=tt.getTemperatureMin().intValue()) && (pending.getTemperatureMax().intValue()>=tt.getTemperatureMax().intValue())) {
				temperatureSpec.add(tt.getIdentifier());
			}
		}
		String keySpec = pending.getLengthDef()+"-"+pending.getFullLoadWeight().toString()+":"+pending.getFullLoadMass().toString()+"-";
		for (int appendIndex=0; appendIndex<temperatureSpec.size(); appendIndex++) {
			keySpec += temperatureSpec.get(appendIndex);
			if (appendIndex != (temperatureSpec.size()-1)) {
				keySpec += ":";
			}
		}
		Query vehicleSpecQuery = new Query();
		Criteria vehicleSpecIdCriteria = Criteria.where("userId").is(pending.getUserId());
		Criteria vehicleSpecCriteria = Criteria.where("keySpec").is(keySpec);
		Criteria andSpecCriteria = new Criteria();
		andSpecCriteria.andOperator(vehicleSpecIdCriteria, vehicleSpecCriteria);
		vehicleSpecQuery.addCriteria(andSpecCriteria);
		VehicleSpec spec = mongoTemplate.findOne(vehicleSpecQuery, VehicleSpec.class);
		if (null == spec) {
			// first create
			spec = new VehicleSpec();
			spec.setUserId(pending.getUserId());
			spec.setKeySpec(keySpec);
			spec.setLengthSpec(pending.getLengthDef());
			spec.setTemperatureSpec(temperatureSpec);
			spec.setStock(Integer.valueOf(1));
			spec.setFullLoadWeight(pending.getFullLoadWeight());
			spec.setFullLoadMass(pending.getFullLoadMass());
			spec.setManInChargeName(pending.getManInChargeName());
			spec.setManInChargePhoneNumber(pending.getManInChargePhoneNumber());
			List<String> vehicles = new ArrayList<String>();
			vehicles.add(pending.getLicensePlate());
			spec.setVehicles(vehicles);
			if (null != user.getIndividualInfo()) {
				spec.setIndividual(user.getIndividualInfo().getLegalName());
			}
			if (null != user.getEnterpriseInfo()) {
				spec.setEnterprise(user.getEnterpriseInfo().getEnterpriseName());
			}
			mongoTemplate.save(spec);
		} else {
			Update vehicleSpecUpdate = new Update();
			vehicleSpecUpdate.inc("stock", Integer.valueOf(1));
			List<String> vehicles = spec.getVehicles();
			vehicles.add(pending.getLicensePlate());
			vehicleSpecUpdate.set("vehicles", vehicles);
			WriteResult vehicleSpecResult = mongoTemplate.updateFirst(vehicleSpecQuery, vehicleSpecUpdate, VehicleSpec.class);
			if (1 != vehicleSpecResult.getN()) {
				return null;
			}
		}
		
		// update binding request as necessary
		Query bindingQuery = new Query();
		Criteria bindingIdCriteria = Criteria.where("pendingId").is(pending.getId());
		bindingQuery.addCriteria(bindingIdCriteria);
		VehicleBindingRequest bindingRequest = mongoTemplate.findOne(bindingQuery, VehicleBindingRequest.class);
		Update bindingUpdate = new Update();
		bindingUpdate.set("status", VehicleBindingRequest.STATUS_OPEN);
		mongoTemplate.updateFirst(bindingQuery, bindingUpdate, VehicleBindingRequest.class);
		
		// generate real time info
		if (null != bindingRequest) {
			PlatformRealtimeInfo realtimeInfo = new PlatformRealtimeInfo();
			realtimeInfo.setModule(PlatformRealtimeInfo.MODULE_USER);
			realtimeInfo.setIdentifier(PlatformRealtimeInfo.IDENTIFIER_DEVICE);
			realtimeInfo.setVehicleLicensePlate(pending.getLicensePlate());
			realtimeInfo.setVehicleLocation(pending.getProvince()+pending.getCity());
			realtimeInfo.setContactName(bindingRequest.getContactName());
			realtimeInfo.setContactPhoneNumber(bindingRequest.getContactPhoneNumber());
			realtimeInfo.setCreationDate(LocalDateTime.now());
			mongoTemplate.save(realtimeInfo);
		}

		// compose message
		Message approvalMessage = new Message();
		approvalMessage.setId(UUID.randomUUID().toString());
		approvalMessage.setDate(new Date());
		approvalMessage.setTitle("您的车辆（" + pending.getLicensePlate() + "）通过审核");
		DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
		approvalMessage.setContent("车辆审核已通过，有效期至" + formatter.format(expiredBy));
		approvalMessage.setPayload(pending.getUserId());
		approvalMessage.setFlag(Message.FLAG_BLUE);
		approvalMessage.setSourceModule("ProductAdmin");
		approvalMessage.setSourceEvent("approveVehiclePending");
		
		String userDevice = user.getMobileDeviceOS();
		if (null != userDevice) {
			// signed in from mobile device
			if (0 == userDevice.compareTo("Android")) {
				if (null == user.getDeviceToken()) {
					logger.warn("device token not updated for user - " + user.getPhoneNumber());
				} else {
					try {
						PushClient client = new PushClient();
						AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
						unicast.setDeviceToken(user.getDeviceToken());
						unicast.setTicker("中国冷链审核");
						unicast.setTitle("您的车辆（" + pending.getLicensePlate() + "）通过审核");
						unicast.setText("车辆审核已通过，有效期至" + formatter.format(expiredBy));
						unicast.goAppAfterOpen();
						unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
						unicast.setProductionMode();
						client.send(unicast);
					} catch (Exception e) {
						logger.warn("failed to post Android notification for user - " + user.getPhoneNumber());
					}
				}
			} else if (0 == userDevice.compareTo("iOS")) {
				if (null == user.getDeviceToken()) {
					logger.warn("device token not updated for user - " + user.getPhoneNumber());
				} else {
					try {
						PushClient client = new PushClient();
						IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
						unicast.setDeviceToken(user.getDeviceToken());
						unicast.setAlert("您的车辆（" + pending.getLicensePlate() + "）通过审核");
						unicast.setBadge(0);
						unicast.setSound("default");
						if (MessageController.isRunningOnProduction) {
							unicast.setProductionMode();
						} else {
							unicast.setTestMode();
						}
						client.send(unicast);
					} catch (Exception e) {
						logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
					}
				}
			}
		}

		return approvalMessage;
	}
	
	public Message approveWarehousePending(String pendingId, Double longitude, Double latitude) {
		// locate warehouse pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		Criteria statusPendingCriteria = Criteria.where("status").is(WarehousePending.STATUS_READY_FOR_REVIEW);
		Criteria andPendingCriteria = new Criteria();
		andPendingCriteria.andOperator(pendingCriteria, statusPendingCriteria);
		pendingQuery.addCriteria(andPendingCriteria);
		WarehousePending pending = mongoTemplate.findOne(pendingQuery, WarehousePending.class);
		if (null == pending) {
			logger.error("approveWarehousePending - cannot locate pending, " + pendingId);
			return null;
		}
		
		// update warehouse pending
		Query updatePendingQuery = new Query();
		Criteria updatePendingCriteria = Criteria.where("id").is(pendingId);
		updatePendingQuery.addCriteria(updatePendingCriteria);
		Update updatePendingUpdate = new Update();
		updatePendingUpdate.set("status", WarehousePending.STATUS_ACCEPTED);
		updatePendingUpdate.set("lastUpdateTime", new Date());
		WriteResult updateResult = mongoTemplate.updateFirst(updatePendingQuery, updatePendingUpdate, WarehousePending.class);
		if (!updateResult.isUpdateOfExisting()) {
			return null;
		}
		
		// locate user
		User user = identityDataManager.findUser(pending.getUserId());
		if (null == user) {
			logger.error("approveWarehousePending - cannot find user: " + pending.getUserId());
			return null;
		}
		
		// upsert warehouse
		Query upsertQuery = new Query();
		Criteria userCriteria = Criteria.where("userId").is(pending.getUserId());
		Criteria nameCriteria = Criteria.where("name").is(pending.getName());
		Criteria andUpsertCriteria = new Criteria();
		andUpsertCriteria.andOperator(userCriteria, nameCriteria);
		upsertQuery.addCriteria(andUpsertCriteria);
		Update upsertUpdate = Warehouse.updateFromWarehousePending(pending);
		upsertUpdate.set("status", Warehouse.WAREHOUSE_STATUS_APPROVED);
		GeoJsonPoint location = new GeoJsonPoint(longitude.doubleValue(), latitude.doubleValue());
		upsertUpdate.set("location", location);
		if (null != user.getIndividualInfo()) {
			upsertUpdate.set("individual", user.getIndividualInfo().getLegalName());
		}
		if (null != user.getEnterpriseInfo()) {
			upsertUpdate.set("enterprise", user.getEnterpriseInfo().getEnterpriseName());
		}
		WriteResult upsertResult = mongoTemplate.upsert(upsertQuery, upsertUpdate, Warehouse.class);
		if (upsertResult.isUpdateOfExisting()) {
			logger.warn("approveWarehousePending - approve warehouse pending with the same name: " + pending.getName());
			return null;
		}
		if (1 != upsertResult.getN()) {
			return null;
		}
		
		// create warehouse spec and update stock
		for (Map<String, Integer> specMap : pending.getTemperatureLevels()) {
			for (String temperatureSpec : specMap.keySet()) {
				String keySpec = pending.getName() + ":" + temperatureSpec;
				Query warehouseSpecQuery = new Query();
				Criteria warehouseSpecIdCriteria = Criteria.where("userId").is(pending.getUserId());
				Criteria warehouseSpecCriteria = Criteria.where("keySpec").is(keySpec);
				Criteria andSpecCriteria = new Criteria();
				andSpecCriteria.andOperator(warehouseSpecIdCriteria, warehouseSpecCriteria);
				warehouseSpecQuery.addCriteria(andSpecCriteria);
				WarehouseSpec spec = mongoTemplate.findOne(warehouseSpecQuery, WarehouseSpec.class);
				if (null == spec) {
					// first create
					spec = new WarehouseSpec();
					spec.setUserId(pending.getUserId());
					spec.setKeySpec(keySpec);
					spec.setTemperatureSpec(temperatureSpec);
					spec.setStock(specMap.get(temperatureSpec));
					spec.setStockTotal(specMap.get(temperatureSpec));
					spec.setName(pending.getName());
					spec.setProvince(pending.getProvince());
					spec.setCity(pending.getCity());
					spec.setDistrict(pending.getDistrict());
					spec.setAddress(pending.getAddress());
					spec.setStockTotal(specMap.get(temperatureSpec));
					spec.setWarehousePhotoId(pending.getWarehousePhotoId());
					if (null != user.getIndividualInfo()) {
						spec.setIndividual(user.getIndividualInfo().getLegalName());
					}
					if (null != user.getEnterpriseInfo()) {
						spec.setEnterprise(user.getEnterpriseInfo().getEnterpriseName());
					}
					spec.setManInChargeName(pending.getManInChargeName());
					spec.setManInChargePhoneNumber(pending.getManInChargePhoneNumber());
					mongoTemplate.save(spec);
				} else {
					// should never go to here?
					Update warehouseSpecUpdate = new Update();
					warehouseSpecUpdate.inc("stock", specMap.get(temperatureSpec));
					warehouseSpecUpdate.inc("stockTotal", specMap.get(temperatureSpec));
					WriteResult warehouseSpecResult = mongoTemplate.updateFirst(warehouseSpecQuery, warehouseSpecUpdate, WarehouseSpec.class);
					if (1 != warehouseSpecResult.getN()) {
						return null;
					}
				}
			}
		}
		
		// update binding request as necessary
		Query bindingQuery = new Query();
		Criteria bindingIdCriteria = Criteria.where("pendingId").is(pending.getId());
		bindingQuery.addCriteria(bindingIdCriteria);
		WarehouseBindingRequest bindingRequest = mongoTemplate.findOne(bindingQuery, WarehouseBindingRequest.class);
		Update bindingUpdate = new Update();
		bindingUpdate.set("status", WarehouseBindingRequest.STATUS_OPEN);
		mongoTemplate.updateFirst(bindingQuery, bindingUpdate, WarehouseBindingRequest.class);
		
		// generate real time info
		if (null != bindingRequest) {
			PlatformRealtimeInfo realtimeInfo = new PlatformRealtimeInfo();
			realtimeInfo.setModule(PlatformRealtimeInfo.MODULE_USER);
			realtimeInfo.setIdentifier(PlatformRealtimeInfo.IDENTIFIER_DEVICE);
			realtimeInfo.setWarehouseName(pending.getName());
			realtimeInfo.setWarehouseLocation(pending.getProvince()+pending.getCity()+pending.getDistrict());
			realtimeInfo.setContactName(bindingRequest.getContactName());
			realtimeInfo.setContactPhoneNumber(bindingRequest.getContactPhoneNumber());
			realtimeInfo.setCreationDate(LocalDateTime.now());
			mongoTemplate.save(realtimeInfo);
		}
		
		// compose message
		Message approvalMessage = new Message();
		approvalMessage.setId(UUID.randomUUID().toString());
		approvalMessage.setDate(new Date());
		approvalMessage.setTitle("您的冷库（" + pending.getName() + "）通过审核");
		approvalMessage.setContent("冷库审核已通过，您可以开始发布库源。");
		approvalMessage.setPayload(pending.getUserId());
		approvalMessage.setFlag(Message.FLAG_BLUE);
		approvalMessage.setSourceModule("ProductAdmin");
		approvalMessage.setSourceEvent("approveWarehousePending");
		
		String userDevice = user.getMobileDeviceOS();
		if (null != userDevice) {
			// signed in from mobile device
			if (0 == userDevice.compareTo("Android")) {
				if (null == user.getDeviceToken()) {
					logger.warn("device token not updated for user - " + user.getPhoneNumber());
				} else {
					try {
						PushClient client = new PushClient();
						AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
						unicast.setDeviceToken(user.getDeviceToken());
						unicast.setTicker("中国冷链审核");
						unicast.setTitle("您的冷库（" + pending.getName() + "）通过审核");
						unicast.setText("冷库审核已通过，您可以开始发布库源。");
						unicast.goAppAfterOpen();
						unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
						unicast.setProductionMode();
						client.send(unicast);
					} catch (Exception e) {
						logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
					}
				}
			} else if (0 == userDevice.compareTo("iOS")) {
				if (null == user.getDeviceToken()) {
					logger.warn("device token not updated for user - " + user.getPhoneNumber());
				} else {
					try {
						PushClient client = new PushClient();
						IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
						unicast.setDeviceToken(user.getDeviceToken());
						unicast.setAlert("您的冷库（" + pending.getName() + "）通过审核");
						unicast.setBadge(0);
						unicast.setSound("default");
						if (MessageController.isRunningOnProduction) {
							unicast.setProductionMode();
						} else {
							unicast.setTestMode();
						}
						client.send(unicast);
					} catch (Exception e) {
						logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
					}
				}
			}
		}
		
		return approvalMessage;
	}
	
	public Message approveProductPending(String pendingId) {
		// locate product pending
		Query pendingQuery = new Query();
		Criteria pendingCriteria = Criteria.where("id").is(pendingId);
		Criteria statusPendingCriteria = Criteria.where("status").is(ProductPending.STATUS_READY_FOR_REVIEW);
		Criteria andPendingCriteria = new Criteria();
		andPendingCriteria.andOperator(pendingCriteria, statusPendingCriteria);
		pendingQuery.addCriteria(andPendingCriteria);
		ProductPending pending = mongoTemplate.findOne(pendingQuery, ProductPending.class);
		if (null == pending) {
			if (logger.isErrorEnabled()) {
				logger.error("approveProductPending - cannot locate pending, " + pendingId);
			}
			return null;
		}
		
		// update product pending
		Query updatePendingQuery = new Query();
		Criteria updatePendingCriteria = Criteria.where("id").is(pendingId);
		updatePendingQuery.addCriteria(updatePendingCriteria);
		Update updatePendingUpdate = new Update();
		updatePendingUpdate.set("status", ProductPending.STATUS_ACCEPTED);
		updatePendingUpdate.set("lastUpdateTime", new Date());
		WriteResult updateResult = mongoTemplate.updateFirst(updatePendingQuery, updatePendingUpdate,
				ProductPending.class);
		if (!updateResult.isUpdateOfExisting()) {
			return null;
		}
		
		// locate user
		User user = identityDataManager.findUser(pending.getUserId());
		if (null == user) {
			return null;
		}
		
		// upsert product
		Query upsertQuery = new Query();
		Criteria userCriteria = Criteria.where("userId").is(pending.getUserId());
		Criteria nameCriteria = Criteria.where("name").is(pending.getName());
		Criteria andUpsertCriteria = new Criteria();
		andUpsertCriteria.andOperator(userCriteria, nameCriteria);
		upsertQuery.addCriteria(andUpsertCriteria);
		Update upsertUpdate = Product.updateFromProductPending(pending);
		upsertUpdate.set("status", Product.PRODUCT_STATUS_APPROVED);
		if (null != user.getIndividualInfo()) {
			upsertUpdate.set("individual", user.getIndividualInfo().getLegalName());
		}
		if (null != user.getEnterpriseInfo()) {
			upsertUpdate.set("enterprise", user.getEnterpriseInfo().getEnterpriseName());
		}
		WriteResult upsertResult = mongoTemplate.upsert(upsertQuery, upsertUpdate, Product.class);
		if (upsertResult.isUpdateOfExisting()) {
			logger.warn("approveWarehousePending - approve warehouse pending with the same name: " + pending.getName());
			return null;
		}
		if (1 != upsertResult.getN()) {
			return null;
		}
		
		// create product spec and update stock
		String keySpec = pending.getName();
		Query productSpecQuery = new Query();
		Criteria productSpecIdCriteria = Criteria.where("userId").is(pending.getUserId());
		Criteria productSpecCriteria = Criteria.where("keySpec").is(keySpec);
		Criteria andSpecCriteria = new Criteria();
		andSpecCriteria.andOperator(productSpecIdCriteria, productSpecCriteria);
		productSpecQuery.addCriteria(andSpecCriteria);
		ProductSpec spec = mongoTemplate.findOne(productSpecQuery, ProductSpec.class);
		if (null == spec) {
			spec = new ProductSpec();
			spec.setUserId(pending.getUserId());
			spec.setKeySpec(keySpec);
			spec.setName(pending.getName());
			spec.setCategory(pending.getCategory());
			spec.setProvince(pending.getProvince());
			spec.setCity(pending.getCity());
			spec.setDistrict(pending.getDistrict());
			spec.setOriginProvince(pending.getOriginProvince());
			spec.setOriginCity(pending.getOriginCity());
			spec.setOriginDistrict(pending.getOriginDistrict());
			spec.setProductPhotoId(pending.getProductPhotoId());
			spec.setStock(Integer.valueOf(0));
			spec.setManInChargeName(pending.getManInChargeName());
			spec.setManInChargePhoneNumber(pending.getManInChargePhoneNumber());
			if (null != user.getIndividualInfo()) {
				spec.setIndividual(user.getIndividualInfo().getLegalName());
			}
			if (null != user.getEnterpriseInfo()) {
				spec.setEnterprise(user.getEnterpriseInfo().getEnterpriseName());
			}
			mongoTemplate.save(spec);
		} else {
			// should never go to here?
			logger.error("approveProductPending - duplicated product spec for " + pending.getName());
			return null;
		}
		
		// compose message
		Message approvalMessage = new Message();
		approvalMessage.setId(UUID.randomUUID().toString());
		approvalMessage.setDate(new Date());
		approvalMessage.setTitle("您的货品（" + pending.getName() + "）通过审核");
		approvalMessage.setContent("货品审核已通过，您可以开始发布货源。");
		approvalMessage.setPayload(pending.getUserId());
		approvalMessage.setFlag(Message.FLAG_BLUE);
		approvalMessage.setSourceModule("ProductAdmin");
		approvalMessage.setSourceEvent("approveProductPending");
		
		String userDevice = user.getMobileDeviceOS();
		if (null != userDevice) {
			// signed in from mobile device
			if (0 == userDevice.compareTo("Android")) {
				if (null == user.getDeviceToken()) {
					logger.warn("device token not updated for user - " + user.getPhoneNumber());
				} else {
					try {
						PushClient client = new PushClient();
						AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
						unicast.setDeviceToken(user.getDeviceToken());
						unicast.setTicker("中国冷链审核");
						unicast.setTitle("您的货品（" + pending.getName() + "）通过审核");
						unicast.setText("货品审核已通过，您可以开始发布货源。");
						unicast.goAppAfterOpen();
						unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
						unicast.setProductionMode();
						client.send(unicast);
					} catch (Exception e) {
						logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
					}
				}
			} else if (0 == userDevice.compareTo("iOS")) {
				if (null == user.getDeviceToken()) {
					logger.warn("device token not updated for user - " + user.getPhoneNumber());
				} else {
					try {
						PushClient client = new PushClient();
						IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
						unicast.setDeviceToken(user.getDeviceToken());
						unicast.setAlert("您的货品（" + pending.getName() + "）通过审核");
						unicast.setBadge(0);
						unicast.setSound("default");
						if (MessageController.isRunningOnProduction) {
							unicast.setProductionMode();
						} else {
							unicast.setTestMode();
						}
						client.send(unicast);
					} catch (Exception e) {
						logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
					}
				}
			}
		}
		
		return approvalMessage;
	}
	
	public boolean addNoteToVehicleBindingRequest(AddNoteToVehicleBindingRequestRequest req) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getReqId());
		query.addCriteria(idCriteria);
		Update update = new Update();
		Map<String, String> note = new HashMap<String, String>();
		DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
		note.put("date", formatter.format(new Date()));
		note.put("note", req.getNote());
		update.addToSet("notes", note);
		
		WriteResult result = mongoTemplate.updateFirst(query, update, VehicleBindingRequest.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		
		return true;
	}
	
	public boolean addNoteToWarehouseBindingRequest(AddNoteToWarehouseBindingRequestRequest req) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getReqId());
		query.addCriteria(idCriteria);
		Update update = new Update();
		Map<String, String> note = new HashMap<String, String>();
		DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
		note.put("date", formatter.format(new Date()));
		note.put("note", req.getNote());
		update.addToSet("notes", note);

		WriteResult result = mongoTemplate.updateFirst(query, update, WarehouseBindingRequest.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		
		return true;
	}
	
	public boolean bindVehicle(BindVehicleRequest req) {
		Query vehicleQuery = new Query();
		Criteria vehicleIdCriteria = Criteria.where("id").is(req.getVehicleId());
		vehicleQuery.addCriteria(vehicleIdCriteria);
		Vehicle vehicle = mongoTemplate.findOne(vehicleQuery, Vehicle.class);
		if (null == vehicle) {
			logger.error("bindVehicle - cannot locate vehicle id:" + req.getVehicleId());
			return false;
		}
		// update device sn
		Update vehicleUpdate = new Update();
		vehicleUpdate.set("deviceSN", req.getDeviceSN());
		WriteResult vehicleResult = mongoTemplate.updateFirst(vehicleQuery, vehicleUpdate, Vehicle.class);
		if (!vehicleResult.isUpdateOfExisting()) {
			logger.error("bindVehicle - vehicle located, but cannot update deviceSN for " + req.getDeviceSN());
			return false;
		}
		
		// update vehicle spec
		Query specQuery = new Query();
		Criteria specUserCriteria = Criteria.where("userId").is(vehicle.getUserId());
		Criteria specPlateCriteria = Criteria.where("vehicles").all(req.getVehicleId());
		Criteria specAndCriteria = new Criteria();
		specAndCriteria.andOperator(specUserCriteria, specPlateCriteria);
		VehicleSpec vehicleSpec = mongoTemplate.findOne(specQuery, VehicleSpec.class);
		if (null == vehicleSpec) {
			logger.error("bindVehicle - cannot locate vehicle spec for " + req.getVehicleId() + ", binding not effective");
			return false;
		}
		Update specUpdate = new Update();
		specUpdate.set("deviceSN", req.getDeviceSN());
		WriteResult specResult = mongoTemplate.updateFirst(specQuery, specUpdate, VehicleSpec.class);
		if (!specResult.isUpdateOfExisting()) {
			logger.error("bindVehicle - vehicle spec located, but cannot update device SN for " + req.getDeviceSN() + ", binding not effective");
			return false;
		}
		
		// generate binding
		VehicleBinding binding = new VehicleBinding();
		binding.setUserId(vehicle.getUserId());
		binding.setLicensePlate(vehicle.getLicensePlate());
		binding.setDeviceSN(req.getDeviceSN());
		binding.setCreationDate(new Date());
		mongoTemplate.save(binding);
		
		return true;
	}
	
	public boolean bindWarehouse(BindWarehouseRequest req) {
		Query warehouseQuery = new Query();
		Criteria warehouseIdCriteria = Criteria.where("id").is(req.getWarehouseId());
		warehouseQuery.addCriteria(warehouseIdCriteria);
		Warehouse warehouse = mongoTemplate.findOne(warehouseQuery, Warehouse.class);
		if (null == warehouse) {
			logger.error("bindWarehouse - cannot locate warehouse, id:" + req.getWarehouseId());
			return false;
		}
		// update device sn
		Update warehouseUpdate = new Update();
		warehouseUpdate.set("deviceSN", req.getDeviceSN());
		WriteResult warehouseResult = mongoTemplate.updateFirst(warehouseQuery, warehouseUpdate, Warehouse.class);
		if (!warehouseResult.isUpdateOfExisting()) {
			logger.error("bindWarehouse - warehouse located, but cannot update deviceSN for " + req.getDeviceSN());
			return false;
		}
		
		// update warehouse spec
		Query specQuery = new Query();
		Criteria specUserCriteria = Criteria.where("userId").is(warehouse.getUserId());
		Criteria specNameCriteria = Criteria.where("name").is(warehouse.getName());
		Criteria specAndCriteria = new Criteria();
		specAndCriteria.andOperator(specUserCriteria, specNameCriteria);
		specQuery.addCriteria(specAndCriteria);
		WarehouseSpec warehouseSpec = mongoTemplate.findOne(specQuery, WarehouseSpec.class);
		if (null == warehouseSpec) {
			logger.error("bindWarehouse - cannot locate warehouse spec for " + req.getWarehouseId() + ", binding not effective");
			return false;
		}
		Update specUpdate = new Update();
		specUpdate.set("deviceSN", req.getDeviceSN());
		WriteResult specResult = mongoTemplate.updateFirst(specQuery, specUpdate, WarehouseSpec.class);
		if (!specResult.isUpdateOfExisting()) {
			logger.error("bindWarehouse - warehouse spec located, but cannot update device SN for " + req.getDeviceSN() + ", binding not effective");
			return false;
		}
		
		// generate binding
		WarehouseBinding binding = new WarehouseBinding();
		binding.setUserId(warehouse.getUserId());
		binding.setName(warehouse.getName());
		binding.setDeviceSN(req.getDeviceSN());
		binding.setCreationDate(new Date());
		mongoTemplate.save(binding);
		
		return true;
	}
	
	public void addIdentityTemplate(AddIdentityTemplateRequest req) {
		AdminIdentityTemplate template = new AdminIdentityTemplate();
		template.setText(req.getText());
		template.setCreationDate(new Date());
		mongoTemplate.save(template);
	}
	
	public void addServiceTemplate(AddServiceTemplateRequest req) {
		AdminServiceTemplate template = new AdminServiceTemplate();
		template.setText(req.getText());
		template.setCreationDate(new Date());
		mongoTemplate.save(template);
	}
	
	public boolean deleteIdentityTemplate(DeleteIdentityTemplateRequest req) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getTemplateId());
		query.addCriteria(idCriteria);
		
		WriteResult result = mongoTemplate.remove(query, AdminIdentityTemplate.class);
		if (1 != result.getN()) {
			return false;
		}
		
		return true;
	}
	
	public boolean deleteServiceTemplate(DeleteServiceTemplateRequest req) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getTemplateId());
		query.addCriteria(idCriteria);
		
		WriteResult result = mongoTemplate.remove(query, AdminServiceTemplate.class);
		if (1 != result.getN()) {
			return false;
		}
		
		return true;
	}
	
	public List<Map<String, String>> queryVehicle(QueryVehicleRequest req) {
		if (null != req.getLicensePlate()) {
			List<Map<String, String>> result = new ArrayList<Map<String, String>>();
			Query query = new Query();
			Criteria vehicleCriteria = Criteria.where("licensePlate").regex("^"+req.getLicensePlate());
			query.addCriteria(vehicleCriteria);
			
			List<Vehicle> vehicles = mongoTemplate.find(query, Vehicle.class);
			logger.info(vehicles.size() + " vehicles matched");;
			for (Vehicle vehicleObject : vehicles) {
				result.add(vehicleObject.toMap());
			}
			return result;
		} else {
			return null;
		}
	}
	
	public List<Map<String, Object>> queryWarehouse(QueryWarehouseRequest req) {
		if (null != req.getName()) {
			List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
			Query query = new Query();
			Criteria warehouseCriteria = Criteria.where("name").regex("^"+req.getName());
			query.addCriteria(warehouseCriteria);
			List<Warehouse> warehouses = mongoTemplate.find(query, Warehouse.class);
			for (Warehouse warehouseObject : warehouses) {
				result.add(warehouseObject.toMap());
			}
			return result;
		} else {
			return null;
		}
	}
	
	public List<Map<String, Object>> queryProduct(QueryProductRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria nameCriteria = Criteria.where("name").is(req.getTerm());
		Criteria sellerCriteria = Criteria.where("userId").is(req.getTerm());
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(nameCriteria, sellerCriteria);
		query.addCriteria(orCriteria);
		
		List<Product> list = mongoTemplate.find(query, Product.class);
		for (Product product : list) {
			result.add(product.toMap());
		}
		
		return result;
	}
	
	public Object queryOnlineVehicles(QueryOnlineVehiclesRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query orderQuery = new Query();
		Criteria deviceSNCriteria = Criteria.where("deviceSN").exists(true);
		Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_TRANSPORT_ONGOING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(deviceSNCriteria, statusCriteria);
		orderQuery.addCriteria(andCriteria);
		List<CompleteVehicleOrder> orderList = mongoTemplate.find(orderQuery, CompleteVehicleOrder.class);
		for (CompleteVehicleOrder vehicleOrder : orderList) {
			Query locationQuery = new Query();
			Criteria orderIdCriteria = Criteria.where("orderId").is(vehicleOrder.getId());
			locationQuery.addCriteria(orderIdCriteria);
			Sort.Order order = new Sort.Order(Sort.Direction.DESC, "dateOfRecord");
			Sort sort = new Sort(order);
			locationQuery.with(sort);
			LocationObject locationObject = mongoTemplate.findOne(locationQuery, LocationObject.class);
			if (null != locationObject) {
				Map<String, Object> map = new HashMap<String, Object>();
				map.put("licensePlate", vehicleOrder.getVehicle().get("licensePlate"));
				map.put("coordinates", locationObject.getLocation().getCoordinates());
				map.put("name", vehicleOrder.getDriver().get("name"));
				map.put("phoneNumber", vehicleOrder.getDriver().get("phoneNumber"));
				result.add(map);
			}
		}
		
		return result;
	}
	public Map<String, Object> queryVehicleLocationDocument(VehicleLocationDocumentRequest req) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("orderId").is(req.getOrderId());
		query.addCriteria(idCriteria);
		LocationDocument document = mongoTemplate.findOne(query, LocationDocument.class);
		if (null == document) {
			return null;
		}
		
		Map<String, Object> map = document.toMap();
		Point point = new Point(req.getLongitude().doubleValue(), req.getLatitude().doubleValue());
		Distance radius = new Distance(req.getRadius().doubleValue(), Metrics.KILOMETERS);
		Circle circle = new Circle(point, radius);
		Query locationQuery = new Query();
		Criteria locationCriteria = Criteria.where("location").withinSphere(circle);
		Criteria orderCriteria = Criteria.where("orderId").is(req.getOrderId());
		Criteria locationAndCriteria = new Criteria();
		locationAndCriteria.andOperator(locationCriteria, orderCriteria);
		locationQuery.addCriteria(locationAndCriteria);
		List<LocationObject> locationObjects = mongoTemplate.find(locationQuery, LocationObject.class);
		if (locationObjects.size() > 0) {
			List<Map<String, Object>> locationList = new ArrayList<Map<String, Object>>();
			for (LocationObject locationObject : locationObjects) {
				locationList.add(locationObject.toMap());
			}
			map.put("locationObjects", locationList);
		}
		
		return map;
	}
	
	public Object queryVehicleOrders(QueryVehicleOrdersRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query orderQuery = new Query();
		Criteria plateCriteria = Criteria.where("vehicle.licensePlate").is(req.getLicensePlate());
		Criteria deviceSNCriteria = Criteria.where("deviceSN").exists(true);
		Criteria statusOnGoingCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_TRANSPORT_ONGOING);
		Criteria statusReviewPendingCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_REVIEW_PENDING);
		Criteria statusCompletedCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_COMPLETED);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(statusOnGoingCriteria, statusReviewPendingCriteria, statusCompletedCriteria);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(plateCriteria, deviceSNCriteria, orCriteria);
		orderQuery.addCriteria(andCriteria);
		Sort.Order order = new Sort.Order(Sort.Direction.DESC, "creationDate");
		Sort sort = new Sort(order);
		orderQuery.with(sort);
		List<CompleteVehicleOrder> orderList = mongoTemplate.find(orderQuery, CompleteVehicleOrder.class);
		for (CompleteVehicleOrder vehicleOrder : orderList) {
			result.add(vehicleOrder.toMap(null));
		}
		
		return result;
	}
	
	public Map<String, Object> queryUser(QueryUserRequest req) {
		// locate user
		Query userQuery = new Query();
		if (null == req.getFilter()) {
			Criteria userIdCriteria = Criteria.where("phoneNumber").is(req.getUserId());
			userQuery.addCriteria(userIdCriteria);
		} else {
			if (0 == req.getFilter().compareTo("individual")) {
				Criteria userIdCriteria = Criteria.where("phoneNumber").is(req.getUserId());
				Criteria individualCriteria = Criteria.where("individualInfo").exists(true);
				Criteria andCriteria = new Criteria();
				andCriteria.andOperator(userIdCriteria, individualCriteria);
				userQuery.addCriteria(andCriteria);
			} else if (0 == req.getFilter().compareTo("enterprise")) {
				Criteria userIdCriteria = Criteria.where("phoneNumber").is(req.getUserId());
				Criteria enterpriseCriteria = Criteria.where("enterpriseInfo").exists(true);
				Criteria andCriteria = new Criteria();
				andCriteria.andOperator(userIdCriteria, enterpriseCriteria);
				userQuery.addCriteria(andCriteria);
			} else {
				Criteria userIdCriteria = Criteria.where("phoneNumber").is(req.getUserId());
				userQuery.addCriteria(userIdCriteria);
			}
		}
		User user = mongoTemplate.findOne(userQuery, User.class);
		if (null == user) {
			logger.error("queryUser - cannot locate user: " + req.getUserId());
			return null;
		}
		Map<String, Object> map = user.toMap();
		
		// locate vehicle
		Query vehicleQuery = new Query();
		Criteria vehicleUserCriteria = Criteria.where("userId").is(user.getPhoneNumber());
		Criteria vehicleStatusCriteria = Criteria.where("status").is(Vehicle.VEHICLE_STATUS_APPROVED);
		Criteria vehicleAndCriteria = new Criteria();
		vehicleAndCriteria.andOperator(vehicleUserCriteria, vehicleStatusCriteria);
		vehicleQuery.addCriteria(vehicleAndCriteria);
		List<Vehicle> vlist = mongoTemplate.find(vehicleQuery, Vehicle.class);
		if ((null!=vlist) && (vlist.size()>0)) {
			List<Object> vehicleList = new ArrayList<Object>();
			for (Vehicle vehicle : vlist) {
				vehicleList.add(vehicle.toMap());
			}
			map.put("vehicle", vehicleList);
		}
		
		// locate warehouse
		Query warehouseQuery = new Query();
		Criteria warehouseUserCriteria = Criteria.where("userId").is(user.getPhoneNumber());
		Criteria warehouseStatusCriteria = Criteria.where("status").is(Warehouse.WAREHOUSE_STATUS_APPROVED);
		Criteria warehouseAndCriteria = new Criteria();
		warehouseAndCriteria.andOperator(warehouseUserCriteria, warehouseStatusCriteria);
		warehouseQuery.addCriteria(warehouseAndCriteria);
		List<Warehouse> wlist = mongoTemplate.find(warehouseQuery, Warehouse.class);
		if ((null!=wlist) && (wlist.size()>0)) {
			List<Object> warehouseList = new ArrayList<Object>();
			for (Warehouse warehouse : wlist) {
				warehouseList.add(warehouse.toMap());
			}
			map.put("warehouse", warehouseList);
		}
		
		// locate product
		Query productQuery = new Query();
		Criteria productUserCriteria = Criteria.where("userId").is(user.getPhoneNumber());
		Criteria productStatusCriteria = Criteria.where("status").is(Product.PRODUCT_STATUS_APPROVED);
		Criteria productAndCriteria = new Criteria();
		productAndCriteria.andOperator(productUserCriteria, productStatusCriteria);
		productQuery.addCriteria(productAndCriteria);
		List<Product> plist = mongoTemplate.find(productQuery, Product.class);
		if ((null!=plist) && (plist.size()>0)) {
			List<Object> productList = new ArrayList<Object>();
			for (Product product : plist) {
				productList.add(product.toMap());
			}
			map.put("product", productList);
		}
		
		return map;
	}
	
	public List<Map<String, Object>> queryIndividualUsers(QueryIndividualUsersRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		ExampleMatcher.GenericPropertyMatcher propertyMatcher = new ExampleMatcher.GenericPropertyMatcher();
		ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("phoneNumber", propertyMatcher.startsWith());
		User user = new User();
		user.setPhoneNumber(req.getPhoneNumber());
		Example<User> example = Example.of(user, matcher);
		Criteria userCriteria = Criteria.byExample(example);
		Criteria individualCriteria = Criteria.where("individualInfo").exists(true);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(userCriteria, individualCriteria);
		query.addCriteria(andCriteria);
		
		List<User> users = mongoTemplate.find(query, User.class);
		for (User userObject : users) {
			result.add(userObject.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> queryEnterpriseUsers (QueryIndividualUsersRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		ExampleMatcher.GenericPropertyMatcher propertyMatcher = new ExampleMatcher.GenericPropertyMatcher();
		ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("phoneNumber", propertyMatcher.startsWith());
		User user = new User();
		user.setPhoneNumber(req.getPhoneNumber());
		Example<User> example = Example.of(user, matcher);
		Criteria userCriteria = Criteria.byExample(example);
		Criteria enterpriseCriteria = Criteria.where("enterpriseInfo").exists(true);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(userCriteria, enterpriseCriteria);
		query.addCriteria(andCriteria);
		
		List<User> users = mongoTemplate.find(query, User.class);
		for (User userObject : users) {
			result.add(userObject.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> infoUpdateForAdminConsole(Date date) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria dateCriteria = Criteria.where("creationDate").gt(date);
		query.addCriteria(dateCriteria);
		
		List<PlatformRealtimeInfo> list = mongoTemplate.find(query, PlatformRealtimeInfo.class);
		for (PlatformRealtimeInfo info : list) {
			result.add(info.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> queryOnlineWarehouse(QueryOnlineWarehouseRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

		Point point = new Point(req.getLongitude().doubleValue(), req.getLatitude().doubleValue());
		Distance radius = new Distance(req.getRadius().doubleValue(), Metrics.KILOMETERS);
		Circle circle = new Circle(point, radius);
		Query query = new Query();
		Criteria criteria = Criteria.where("location").withinSphere(circle);
		query.addCriteria(criteria);

		List<Warehouse> warehouseList = mongoTemplate.find(query, Warehouse.class);
		for (Warehouse warehouse : warehouseList) {
			result.add(warehouse.toMap());
		}
		
		return result;
	}
	
	public boolean processedVehicleBindingRequest(ProcessedVehicleBindingRequestRequest req) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getReqId());
		query.addCriteria(idCriteria);
		Update update = new Update();
		update.set("status", VehicleBindingRequest.STATUS_CLOSED);
		
		WriteResult result = mongoTemplate.updateFirst(query, update, VehicleBindingRequest.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		
		return true;
	}
	
	public boolean processedWarehouseBindingRequest(ProcessedWarehouseBindingRequestRequest req) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getReqId());
		query.addCriteria(idCriteria);
		Update update = new Update();
		update.set("status", VehicleBindingRequest.STATUS_CLOSED);

		WriteResult result = mongoTemplate.updateFirst(query, update, WarehouseBindingRequest.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		
		return true;
	}
	
	public boolean resolveAppeal(ResolveAppealRequest req) {
		Query orderQuery = new Query();
		Criteria orderIdCriteria = Criteria.where("id").is(req.getOrderId());
		orderQuery.addCriteria(orderIdCriteria);
		CompleteVehicleOrder vehicleOrder = mongoTemplate.findOne(orderQuery, CompleteVehicleOrder.class);
		if (null != vehicleOrder) {
			if (0 != vehicleOrder.getStatus().compareTo(CompleteVehicleOrder.STATUS_APPEAL_ONGOING)) {
				logger.warn("resolveAppeal - invalid state for vehicle order, id: " + req.getOrderId());
				return false;
			}
			if (req.getBuyerAmount().intValue()+req.getSellerAmount().intValue() != vehicleOrder.getPrice().intValue()) {
				logger.warn("resolveAppeal - amount error");
				return false;
			}
			// update buyer balance
			Query buyerQuery = new Query();
			Criteria buyerPhoneNumberCriteria = Criteria.where("phoneNumber").is(vehicleOrder.getBuyerId());
			buyerQuery.addCriteria(buyerPhoneNumberCriteria);
			Update buyerUpdate = new Update();
			buyerUpdate.inc("balance", req.getBuyerAmount());
			buyerUpdate.inc("withdrawBalance", req.getBuyerAmount());
			WriteResult buyerResult = mongoTemplate.updateFirst(buyerQuery, buyerUpdate, User.class);
			if (!buyerResult.isUpdateOfExisting()) {
				return false;
			}
			
			// update seller balance
			Query sellerQuery = new Query();
			Criteria sellerPhoneNumberCriteria = Criteria.where("phoneNumber").is(vehicleOrder.getSellerId());
			sellerQuery.addCriteria(sellerPhoneNumberCriteria);
			Update sellerUpdate = new Update();
			buyerUpdate.inc("balance", req.getSellerAmount());
			buyerUpdate.inc("withdrawBalance", req.getSellerAmount());
			WriteResult sellerResult = mongoTemplate.updateFirst(sellerQuery, sellerUpdate, User.class);
			if (!sellerResult.isUpdateOfExisting()) {
				logger.error("resolveAppeal - failed to update seller balance, needs to be manually resolved, seller id: " + vehicleOrder.getSellerId() + " order id: " + req.getOrderId());
			}
			// mark this order as resolved
			Update orderUpdate = new Update();
			orderUpdate.set("status", CompleteVehicleOrder.STATUS_COMPLETED);
			WriteResult orderResult = mongoTemplate.updateFirst(orderQuery, orderUpdate, CompleteVehicleOrder.class);
			if (!orderResult.isUpdateOfExisting()) {
				logger.error("resolveAppeal - failed to update order status, needs manually check, order id: " + req.getOrderId());
			}
			// create appeal result
			AppealResult appealResult = new AppealResult();
			appealResult.setBuyerAmount(req.getBuyerAmount());
			appealResult.setSellerAmount(req.getSellerAmount());
			appealResult.setResultStatement(req.getResultStatement());
			appealResult.setOrderObject(vehicleOrder.toMap(null));
			appealResult.setCreationDate(new Date());
			mongoTemplate.save(appealResult);
			// compose message
			Message appealMessage = new Message();
			appealMessage.setId(UUID.randomUUID().toString());
			appealMessage.setDate(new Date());
			appealMessage.setTitle("您的订单申诉已解决");
			appealMessage.setContent("您的订单（订单号：" + vehicleOrder.getAlipayOrderNumber() + "）申诉已处理，处理结果：" + req.getResultStatement());
			appealMessage.setFlag(Message.FLAG_BLUE);
			appealMessage.setSourceModule("OrderAdmin");
			appealMessage.setSourceEvent("resolveAppeal");
			messageController.postMessage(appealMessage, vehicleOrder.getBuyerId());
			messageController.postMessage(appealMessage, vehicleOrder.getSellerId());
			return true;
		} else {
			WarehouseOrder warehouseOrder = mongoTemplate.findOne(orderQuery, WarehouseOrder.class);
			if (null != warehouseOrder) {
				if (0 != warehouseOrder.getStatus().compareTo(WarehouseOrder.STATUS_APPEAL_ONGOING)) {
					logger.warn("resolveAppeal - invalid state for warehouse order, id: " + req.getOrderId());
					return false;
				}
				int days = DateUtil.daysBetween(warehouseOrder.getStartDate(), warehouseOrder.getUntilDate()).intValue();
				if (req.getBuyerAmount().intValue()+req.getSellerAmount().intValue() != warehouseOrder.getUnitPrice().intValue()*warehouseOrder.getVolume().intValue()*days) {
					logger.warn("resolveAppeal - amount error");
					return false;
				}
				// update buyer balance
				Query buyerQuery = new Query();
				Criteria buyerPhoneNumberCriteria = Criteria.where("phoneNumber").is(warehouseOrder.getBuyerId());
				buyerQuery.addCriteria(buyerPhoneNumberCriteria);
				Update buyerUpdate = new Update();
				buyerUpdate.inc("balance", req.getBuyerAmount());
				buyerUpdate.inc("withdrawBalance", req.getBuyerAmount());
				WriteResult buyerResult = mongoTemplate.updateFirst(buyerQuery, buyerUpdate, User.class);
				if (!buyerResult.isUpdateOfExisting()) {
					return false;
				}
				
				// update seller balance
				Query sellerQuery = new Query();
				Criteria sellerPhoneNumberCriteria = Criteria.where("phoneNumber").is(warehouseOrder.getSellerId());
				sellerQuery.addCriteria(sellerPhoneNumberCriteria);
				Update sellerUpdate = new Update();
				buyerUpdate.inc("balance", req.getSellerAmount());
				buyerUpdate.inc("withdrawBalance", req.getSellerAmount());
				WriteResult sellerResult = mongoTemplate.updateFirst(sellerQuery, sellerUpdate, User.class);
				if (!sellerResult.isUpdateOfExisting()) {
					logger.error("resolveAppeal - failed to update seller balance, needs to be manually resolved, seller id: " + warehouseOrder.getSellerId() + " order id: " + req.getOrderId());
				}
				// mark this order as resolved
				Update orderUpdate = new Update();
				orderUpdate.set("status", WarehouseOrder.STATUS_COMPLETED);
				WriteResult orderResult = mongoTemplate.updateFirst(orderQuery, orderUpdate, WarehouseOrder.class);
				if (!orderResult.isUpdateOfExisting()) {
					logger.error("resolveAppeal - failed to update order status, needs manually check, order id: " + req.getOrderId());
				}
				// create appeal result
				AppealResult appealResult = new AppealResult();
				appealResult.setBuyerAmount(req.getBuyerAmount());
				appealResult.setSellerAmount(req.getSellerAmount());
				appealResult.setResultStatement(req.getResultStatement());
				appealResult.setOrderObject(warehouseOrder.toMap(null));
				appealResult.setCreationDate(new Date());
				mongoTemplate.save(appealResult);
				// compose message
				Message appealMessage = new Message();
				appealMessage.setId(UUID.randomUUID().toString());
				appealMessage.setDate(new Date());
				appealMessage.setTitle("您的订单申诉已解决");
				appealMessage.setContent("您的订单（订单号：" + warehouseOrder.getAlipayOrderNumber() + "）申诉已处理，处理结果：" + req.getResultStatement());
				appealMessage.setFlag(Message.FLAG_BLUE);
				appealMessage.setSourceModule("OrderAdmin");
				appealMessage.setSourceEvent("resolveAppeal");
				messageController.postMessage(appealMessage, warehouseOrder.getBuyerId());
				messageController.postMessage(appealMessage, warehouseOrder.getSellerId());
				return true;
			} else {
				ProductOrder productOrder = mongoTemplate.findOne(orderQuery, ProductOrder.class);
				if (null != productOrder) {
					if (0 != productOrder.getStatus().compareTo(ProductOrder.STATUS_APPEAL_ONGOING)) {
						logger.warn("resolveAppeal - invalid state for product order, id: " + req.getOrderId());
						return false;
					}
					if (req.getBuyerAmount().intValue()+req.getSellerAmount().intValue() != productOrder.getUnitPrice().intValue()*productOrder.getVolume().intValue()) {
						logger.warn("resolveAppeal - amount error");
						return false;
					}
					// update buyer balance
					Query buyerQuery = new Query();
					Criteria buyerPhoneNumberCriteria = Criteria.where("phoneNumber").is(productOrder.getBuyerId());
					buyerQuery.addCriteria(buyerPhoneNumberCriteria);
					Update buyerUpdate = new Update();
					buyerUpdate.inc("balance", req.getBuyerAmount());
					buyerUpdate.inc("withdrawBalance", req.getBuyerAmount());
					WriteResult buyerResult = mongoTemplate.updateFirst(buyerQuery, buyerUpdate, User.class);
					if (!buyerResult.isUpdateOfExisting()) {
						return false;
					}
					
					// update seller balance
					Query sellerQuery = new Query();
					Criteria sellerPhoneNumberCriteria = Criteria.where("phoneNumber").is(productOrder.getSellerId());
					sellerQuery.addCriteria(sellerPhoneNumberCriteria);
					Update sellerUpdate = new Update();
					buyerUpdate.inc("balance", req.getSellerAmount());
					buyerUpdate.inc("withdrawBalance", req.getSellerAmount());
					WriteResult sellerResult = mongoTemplate.updateFirst(sellerQuery, sellerUpdate, User.class);
					if (!sellerResult.isUpdateOfExisting()) {
						logger.error("resolveAppeal - failed to update seller balance, needs to be manually resolved, seller id: " + productOrder.getSellerId() + " order id: " + req.getOrderId());
					}
					// mark this order as resolved
					Update orderUpdate = new Update();
					orderUpdate.set("status", ProductOrder.STATUS_COMPLETED);
					WriteResult orderResult = mongoTemplate.updateFirst(orderQuery, orderUpdate, ProductOrder.class);
					if (!orderResult.isUpdateOfExisting()) {
						logger.error("resolveAppeal - failed to update order status, needs manually check, order id: " + req.getOrderId());
					}
					// create appeal result
					AppealResult appealResult = new AppealResult();
					appealResult.setBuyerAmount(req.getBuyerAmount());
					appealResult.setSellerAmount(req.getSellerAmount());
					appealResult.setResultStatement(req.getResultStatement());
					appealResult.setOrderObject(productOrder.toMap(null));
					appealResult.setCreationDate(new Date());
					mongoTemplate.save(appealResult);
					// compose message
					Message appealMessage = new Message();
					appealMessage.setId(UUID.randomUUID().toString());
					appealMessage.setDate(new Date());
					appealMessage.setTitle("您的订单申诉已解决");
					appealMessage.setContent("您的订单（订单号：" + productOrder.getAlipayOrderNumber() + "）申诉已处理，处理结果：" + req.getResultStatement());
					appealMessage.setFlag(Message.FLAG_BLUE);
					appealMessage.setSourceModule("OrderAdmin");
					appealMessage.setSourceEvent("resolveAppeal");
					messageController.postMessage(appealMessage, productOrder.getBuyerId());
					messageController.postMessage(appealMessage, productOrder.getSellerId());
					return true;
				} else {
					logger.warn("resolveAppeal - cannot locate order, id: " + req.getOrderId());
					return false;
				}
			}
		}
	}
	
	public String heartbeatMockup(HeartbeatMockupRequest req) {
		String receipt = UUID.randomUUID().toString();
		Bootstrap bootstrap = new Bootstrap();
		EventLoopGroup clientGroup = new EpollEventLoopGroup();
		bootstrap.group(clientGroup);
		bootstrap.channel(EpollSocketChannel.class);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
		bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
            	MockupInboundAdapter inboundAdapter = new MockupInboundAdapter();
            	inboundAdapter.setReceipt(receipt);
            	inboundAdapter.setReqType(MockupInboundAdapter.REQUEST_TYPE_HEARTBEAT);
            	inboundAdapter.setRedisTemplate(redisTemplate);
            	inboundAdapter.setDeviceSN(req.getDeviceSN());
                ch.pipeline().addLast(inboundAdapter);
            }
        });
		try {
			ChannelFuture future = bootstrap.connect("127.0.0.1", 28888).sync();
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			clientGroup.shutdownGracefully();
		}
		
		return receipt;
	}
	
	public String locationMockup(LocationMockupRequest req) {
		String receipt = UUID.randomUUID().toString();
		Bootstrap bootstrap = new Bootstrap();
		EventLoopGroup clientGroup = new EpollEventLoopGroup();
		bootstrap.group(clientGroup);
		bootstrap.channel(EpollSocketChannel.class);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
		bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
            	MockupInboundAdapter inboundAdapter = new MockupInboundAdapter();
            	inboundAdapter.setReceipt(receipt);
            	inboundAdapter.setReqType(MockupInboundAdapter.REQUEST_TYPE_LOCATION_REPORTING);
            	inboundAdapter.setRedisTemplate(redisTemplate);
            	inboundAdapter.setDeviceSN(req.getDeviceSN());
            	inboundAdapter.setLongitude(req.getLongitude());
            	inboundAdapter.setLatitude(req.getLatitude());
                ch.pipeline().addLast(inboundAdapter);
            }
        });
		try {
			ChannelFuture future = bootstrap.connect("127.0.0.1", 28888).sync();
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			clientGroup.shutdownGracefully();
		}
		
		return receipt;
	}
	
	public String temperatureMockup(TemperatureMockupRequest req) {
		String receipt = UUID.randomUUID().toString();
		Bootstrap bootstrap = new Bootstrap();
		EventLoopGroup clientGroup = new EpollEventLoopGroup();
		bootstrap.group(clientGroup);
		bootstrap.channel(EpollSocketChannel.class);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
		bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
            	MockupInboundAdapter inboundAdapter = new MockupInboundAdapter();
            	inboundAdapter.setReceipt(receipt);
            	inboundAdapter.setReqType(MockupInboundAdapter.REQUEST_TYPE_TEMPERATURE_REPORTING);
            	inboundAdapter.setRedisTemplate(redisTemplate);
            	inboundAdapter.setDeviceSN(req.getDeviceSN());
            	inboundAdapter.setTemperature(req.getTemperature());
            	inboundAdapter.setLongitude(req.getLongitude());
            	inboundAdapter.setLatitude(req.getLatitude());
                ch.pipeline().addLast(inboundAdapter);
            }
        });
		try {
			ChannelFuture future = bootstrap.connect("127.0.0.1", 28888).sync();
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			clientGroup.shutdownGracefully();
		}
		
		return receipt;
	}
	
	public String humidityMockup(HumidityMockupRequest req) {
		String receipt = UUID.randomUUID().toString();
		Bootstrap bootstrap = new Bootstrap();
		EventLoopGroup clientGroup = new EpollEventLoopGroup();
		bootstrap.group(clientGroup);
		bootstrap.channel(EpollSocketChannel.class);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
		bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
            	MockupInboundAdapter inboundAdapter = new MockupInboundAdapter();
            	inboundAdapter.setReceipt(receipt);
            	inboundAdapter.setReqType(MockupInboundAdapter.REQUEST_TYPE_HUMIDITY_REPORTING);
            	inboundAdapter.setRedisTemplate(redisTemplate);
            	inboundAdapter.setDeviceSN(req.getDeviceSN());
            	inboundAdapter.setHumidity(req.getHumidity());
            	inboundAdapter.setLongitude(req.getLongitude());
            	inboundAdapter.setLatitude(req.getLatitude());
                ch.pipeline().addLast(inboundAdapter);
            }
        });
		try {
			ChannelFuture future = bootstrap.connect("127.0.0.1", 28888).sync();
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			clientGroup.shutdownGracefully();
		}
		
		return receipt;
	}
	
	public String mockupResult(MockupResultRequest req) {
		String reqString = (String)redisTemplate.opsForValue().get(req.getReceipt()+"_outbound");
		String resString = (String)redisTemplate.opsForValue().get(req.getReceipt()+"_inbound");
		if ((null==reqString) || (null==resString)) {
			return null;
		}
		
		return reqString + "\n" + resString;
	}

	@PostConstruct
	public void populatePredefinedData() {
		addDefaultAdmin();
	}

	protected void addDefaultAdmin() {
		Admin defaultAdmin = findAdmin("13958123303");
		if (null == defaultAdmin) {
			defaultAdmin = new Admin();
			defaultAdmin.setLoginName("13958123303");
			defaultAdmin.setPassword("Cf223344");
			mongoTemplate.save(defaultAdmin);
		}
		Admin zhangWeiDong = findAdmin("18643106991");
		if (null == zhangWeiDong) {
			zhangWeiDong = new Admin();
			zhangWeiDong.setLoginName("18643106991");
			zhangWeiDong.setPassword("123456");
			mongoTemplate.save(zhangWeiDong);
		}

		return;
	}
	
	@Autowired
	private IdentityDataManager identityDataManager;

	@Autowired
	private MongoTemplate mongoTemplate;
	
	@Autowired
	private RedisTemplate<String, Object> redisTemplate;
	
	@Autowired
	private MessageController messageController;

	private static final Logger logger = LoggerFactory.getLogger(AdminDataManager.class);

}
